/*
 * Author: vdaras
 */


#include <SDL/SDL_video.h>
#include "Canvas.h"
#include "Color.h"
#include "SDLException.h"
#include "GLColor.h"

namespace sdl
{

/*
 * Constructor allocates the surface but if it fails an exception is thrown.
 * Since no color is specified the canvas will be transparent.
 *
 * @param
 * - width: surface's width
 * - height: surface's height
 */

Canvas::Canvas(int width, int height)
{
    //create surface, if it fails
    if( !Create( width, height ) )
    {
        //indicate failure
        throw Exception( SDL_GetError( ) );
    }
}

/*
 * Constructor allocates the surface but if it fails an exception is thrown
 * indicating the error.
 *
 * @param
 * - width: surface's width
 * - height: surface's height
 * - background: surface's background color
 */

Canvas::Canvas(int width, int height, const Color &background)
{
    //create surface, if it fails
    if( !Create( width, height, background ) )
    {
        //indicate failure
        throw Exception( SDL_GetError( ) );
    }
}

Canvas::~Canvas()
{
}

bool Canvas::Render()
{
    //convert this bufer into a texture wich opengl can render
    GLuint id = this->ConvertToTexture( );
    //set the state to the texture
    glBindTexture( GL_TEXTURE_2D, id );

    //begin rendering
    glBegin( GL_QUADS );
    {
        gl::colors::WHITE.Apply( );
        //set up the vertecies
        GLfloat v0[2] = { 0, 0 };
        GLfloat v1[2] = { GetBuffer( )->w, 0 };
        GLfloat v2[2] = { 0, GetBuffer( )->h };
        GLfloat v3[2] = { GetBuffer( )->w, GetBuffer( )->h };

        //rendering time
        glTexCoord2i( 0, 0 );
        glVertex2fv( v0 );
        glTexCoord2i( 1, 0 );
        glVertex2fv( v1 );
        glTexCoord2i( 1, 1 );
        glVertex2fv( v3 );
        glTexCoord2i( 0, 1 );
        glVertex2fv( v2 );
    }
    glEnd( );

    glDeleteTextures( 1, &id );

    //    glPixelZoom( 1, -1 );
    //    gl::colors::WHITE.Apply( );
    //    glRasterPos2i( 0, 0 );
    //    
    //#if SDL_BYTEORDER == SDL_BIG_ENDIAN
    //    glDrawPixels( GetBuffer( )->w, GetBuffer( )->h, GL_BGRA, GL_UNSIGNED_BYTE, GetBuffer( )->pixels );
    //#else
    //    glDrawPixels( GetBuffer( )->w, GetBuffer( )->h, GL_RGBA, GL_UNSIGNED_BYTE, GetBuffer( )->pixels );
    //#endif
    //
    //    glFlush( );
    return true;
}

/**
 * Fills the canvas with transparent pixels.
 */

void Canvas::FillTransparent()
{
    SDL_Surface *buffer = this->GetBuffer( );
    SDL_Rect rect = { 0, 0, buffer->w, buffer->h };
    Uint32 transparentColor = SDL_MapRGBA( buffer->format, 0, 0, 0, 0 );
    SDL_FillRect( buffer, &rect, transparentColor );
}

/*
 * This method allocates a transparent surface using the SDL API.
 * If an error occurs it returns failure which the caller should handle.
 *
 * @param
 * - width: surface's width
 * - height: surface's height
 */

bool Canvas::Create(int width, int height)
{
    int rmask, gmask, bmask, amask;

#if SDL_BYTEORDER == SDL_BIG_ENDIAN
    rmask = 0xff000000;
    gmask = 0x00ff0000;
    bmask = 0x0000ff00;
    amask = 0x000000ff;
#else
    rmask = 0x000000ff;
    gmask = 0x0000ff00;
    bmask = 0x00ff0000;
    amask = 0xff000000;
#endif

    //allocate a temporary primitive surface
    SDL_Surface *temp = SDL_CreateRGBSurface( SDL_HWSURFACE, width, height, 32, rmask, gmask, bmask, amask );

    //if allocation was successful
    if( temp != NULL )
    {
        //optimize temporary surface 
        SDL_Surface *optimizedSurface = SDL_DisplayFormatAlpha( temp );

        SDL_FreeSurface( temp );

        if( optimizedSurface != NULL )
        {
            //make the surface transparent
            SDL_Rect rect = { 0, 0, width, height };

            Uint32 color = SDL_MapRGBA( optimizedSurface->format, 0, 0, 0, 0 );

            SDL_FillRect( optimizedSurface, &rect, color );

            //set the Surface's buffer to the optimized surface
            SetBuffer( optimizedSurface );
            //return success
            return true;
        }
    }
    //the surface could not be allocated, return failure
    return false;
}

/*
 * This method allocates a surface using the SDL API and then fills it with
 * the desired background color. If an error occurs it returns failure which the
 * caller should handle.
 *
 * @param
 * - width: surface's width
 * - height: surface's height
 * - background: surface's background color
 */

bool Canvas::Create(int width, int height, const Color& background)
{
    int rmask, gmask, bmask, amask;

#if SDL_BYTEORDER == SDL_BIG_ENDIAN
    rmask = 0xff000000;
    gmask = 0x00ff0000;
    bmask = 0x0000ff00;
    amask = 0x000000ff;
#else
    rmask = 0x000000ff;
    gmask = 0x0000ff00;
    bmask = 0x00ff0000;
    amask = 0xff000000;
#endif

    //allocate a temporary primitive surface
    SDL_Surface *temp = SDL_CreateRGBSurface( SDL_HWSURFACE, width, height, 32, rmask, gmask, bmask, amask );

    //if allocation was successful
    if( temp != NULL )
    {
        SDL_Surface *optimizedSurface = SDL_DisplayFormat( temp );

        SDL_FreeSurface( temp );

        //set the Surface's buffer to the opimized surface
        if( optimizedSurface != NULL )
        {
            //fill the surface using the background color
            SDL_Rect rect = { 0, 0, width, height };

            Uint32 color = SDL_MapRGBA( optimizedSurface->format, background.GetR( ), background.GetG( ), background.GetB( ), 255 );

            SDL_FillRect( optimizedSurface, &rect, color );

            SetBuffer( optimizedSurface );
        }
        //return success
        return true;
    }
    //the surface could not be allocated, return failure
    return false;
}

};
